//
//  EAGLView.m
//  BioBreak
//
//  Created by terry franguiadakis on 2/17/09.
//  Copyright __MyCompanyName__ 2009. All rights reserved.
//


#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import <OpenGLES/ES1/gl.h>
#import <AudioToolbox/AudioServices.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>
#import "Texture2D.h"
#import "EAGLView.h"

#include "camera.h"
#include "texture.h"
#include "level.h"
#include "map.h"
#import "render.h"
#include "engine.h"
#include "soundmgr.h"
#include "touchmgr.h"
#include "uimgr.h"
#include "go_path.h"
#import "importutils.h"
#import "ModelParser.h"

#define USE_DEPTH_BUFFER 1 

static bool isAlertShowing;
 
// ------------------------------------------------------------------------------
#pragma mark systemIOfunctions

static unsigned int truncPowerOf2(unsigned int x)
{
    int i = 0;
    while (x = x>>1) i++;
    return (1<<i);
}

static void GetPathForResource(const char* i_filename, NSString** o_path)
{
	NSString* filename = [[NSString alloc] initWithCString:i_filename];      
	NSBundle* bundle = [NSBundle mainBundle];
	(*o_path) = [bundle pathForResource:filename ofType:nil];
}

// ------------------------------------------------------------------------------

//Example: SystemMessage("Testing", "This is working", "Ok", "Cancel");
static void SystemMessage(const char* title, const char* message, const char* acceptString, const char* cancelString)
{
	NSString* nsTitle = [NSString stringWithCString:title];
	NSString* nsMessage = [NSString stringWithCString:message];
	NSString* nsAccept = [NSString stringWithCString:acceptString];
	UIAlertView* myAlertView;
	if (cancelString)
	{
		NSString* nsCancel = [NSString stringWithCString:cancelString];
		myAlertView = [[UIAlertView alloc] initWithTitle:nsTitle message:nsMessage delegate:[EAGLView sharedSingleton] cancelButtonTitle:nsCancel otherButtonTitles:nsAccept, nil];
	}
	else
	{
		myAlertView = [[UIAlertView alloc] initWithTitle:nsTitle message:nsMessage delegate:[EAGLView sharedSingleton] cancelButtonTitle:nil otherButtonTitles:nsAccept, nil];
	}
	
	isAlertShowing = true;
	[myAlertView show];
	[myAlertView release];
}

static bool IsSystemMessageShowing()
{
	return isAlertShowing;
}

static void SystemLoadLevelFileSetupCommands(NSArray* i_setupCommands, CLevel* io_level)
{
	//do all the setup commands
	//always ignore the first and last
	int setupCommandCount = [i_setupCommands count];
	for (int i = 0; i < setupCommandCount; i++) 
	{		
		NSString *setupstring = [i_setupCommands objectAtIndex:i];
		
		//skip empty commands
		if (setupstring == nil || [setupstring isEqualToString:@""] )
		{
			continue;
		}
		
		//split update command into parameters
		NSArray *keys = [setupstring componentsSeparatedByString: @" "];
		
		//resolve mineral placement
		NSString* key = [keys objectAtIndex:0];
		if ([key caseInsensitiveCompare:@"minerals"] == NSOrderedSame)
		{
			NSArray *parameters = [setupstring componentsSeparatedByString: @" "];			
			float x = [[parameters objectAtIndex:1] floatValue];
			float y = [[parameters objectAtIndex:2] floatValue];
			int mineralCount = [[parameters objectAtIndex:3] floatValue];
			io_level->On_AddMinerals(x, y, mineralCount);
		}
		else if ([key caseInsensitiveCompare:@"mineralcount"] == NSOrderedSame)
		{
			float mineralCount = [[keys objectAtIndex:1] floatValue];
			io_level->On_MineralCount(mineralCount);
		}
		else if ([key caseInsensitiveCompare:@"path"] == NSOrderedSame)
		{
			//create a path at this location
			CGoPath* path = (CGoPath*)g_gomgr.CreateGo(EClass_Path, Vector2f::Zero);
			
			//parse the parameters
			NSArray *parameters = [setupstring componentsSeparatedByString: @" "];
			
			//parse the name of the path
			NSString* nameNS = [parameters objectAtIndex:1];
			const char* goName = [nameNS UTF8String];
			path->SetName(goName);
			
			// read in all the points
			int pointCount = ([parameters count] - 2) / 2;
			Vector2f pointToAdd;
			for(int iPoint = 0; iPoint < pointCount; ++iPoint)
			{
				//point starts at 2 and contain 2 floats (2*index + 2)
				int pointIndexStart = 2*iPoint + 2;
				
				pointToAdd.x = [[parameters objectAtIndex:pointIndexStart] floatValue];
				pointToAdd.y = [[parameters objectAtIndex:(pointIndexStart + 1)] floatValue];
				path->AddPathPoint(pointToAdd);
			}
		}
		else if ([key caseInsensitiveCompare:@"spawn"] == NSOrderedSame)
		{
			NSArray *parameters = [setupstring componentsSeparatedByString: @" "];			
			NSString* classNS = [parameters objectAtIndex:1];
			const char* className = [classNS UTF8String];
			float x = [[parameters objectAtIndex:2] floatValue];
			float y = [[parameters objectAtIndex:3] floatValue];
			io_level->On_Spawn(className, x, y);
		}
		else
		{
			NSLog(@"unknown setup level command ");
			NSLog(setupstring);
		}
	}
}

static void SystemLoadLevelFileCommands(NSArray* i_commands, CLevel* io_level)
{
	//do all the commands
	//always ignore the first and last
	int setupCommandCount = [i_commands count];
	for (int i = 0; i < setupCommandCount; i++) 
	{		
		NSString *setupstring = [i_commands objectAtIndex:i];
		
		//skip empty commands
		if (setupstring == nil || [setupstring isEqualToString:@""] )
		{
			continue;
		}
		
		//split update command into parameters
		NSArray *keys = [setupstring componentsSeparatedByString: @" "];
		
		//resolve mineral placement
		NSString* key = [keys objectAtIndex:0];
		if ([key caseInsensitiveCompare:@"wave"] == NSOrderedSame)
		{
			NSArray *parameters = [setupstring componentsSeparatedByString: @" "];			
			NSString* waveFile = [parameters objectAtIndex:1];
			const char *fileName = [waveFile UTF8String];
			float x = [[parameters objectAtIndex:2] floatValue];
			float y = [[parameters objectAtIndex:3] floatValue];
			io_level->On_AddWave(fileName, x, y);
		}
		else if ([key caseInsensitiveCompare:@"timer"] == NSOrderedSame)
		{
			float duration = [[keys objectAtIndex:1] floatValue];
			io_level->On_DisplayTimer(duration);
		}
		else if ([key caseInsensitiveCompare:@"message"] == NSOrderedSame)
		{
			NSArray *parameters = [setupstring componentsSeparatedByString: @"\""];			
			const char* title = [[parameters objectAtIndex:1] UTF8String];
			const char* message = [[parameters objectAtIndex:3] UTF8String];
			const char* accept = [[parameters objectAtIndex:5] UTF8String];
			
			io_level->On_AddSystemMessage(title, message, accept);
			
		}
		else if ([key caseInsensitiveCompare:@"goal"] == NSOrderedSame)
		{
			//figure out what type of goal
			NSString* goalString = [keys objectAtIndex:1];
			if ([goalString caseInsensitiveCompare:@"collect"] == NSOrderedSame)
			{
				float mineralCount = [[keys objectAtIndex:2] floatValue];
				io_level->On_GoalCollectMinerals(mineralCount);
			}
			else if ([goalString caseInsensitiveCompare:@"survive"] == NSOrderedSame)
			{
				io_level->On_GoalSurviveWave();
			}
			else
			{
				NSLog(@"unknown level goal ");
				NSLog(goalString);
			}
		}
		else if ([key caseInsensitiveCompare:@"pause"] == NSOrderedSame)
		{
			NSArray *parameters = [setupstring componentsSeparatedByString: @" "];			
			float secs = [[parameters objectAtIndex:1] floatValue];
			io_level->On_AddPause(secs);
		}
		else
		{
			NSLog(@"unknown level command ");
			NSLog(setupstring);
		}
	}
}

static bool SystemLoadLevelFile(const char* i_filename, CLevel* io_level)
{
	NSString* filePath;
	GetPathForResource(i_filename, &filePath);
	
	NSString *aString = [NSString stringWithContentsOfFile:filePath];
	NSArray *strings1 = [aString componentsSeparatedByString: @"Setup:"];
	NSArray *strings2 = [aString componentsSeparatedByString: @"Commands:"];
	
	if ([strings1 count] > 1)
	{
		NSArray *setupStrings = [[strings1 objectAtIndex:1] componentsSeparatedByString: @"\n"];
		SystemLoadLevelFileSetupCommands(setupStrings, io_level);
	}
	
	if ([strings2 count] > 1)
	{
		NSArray *commandStrings = [[strings2 objectAtIndex:1]  componentsSeparatedByString: @"\n"];
		SystemLoadLevelFileCommands(commandStrings, io_level);
	}	
	return true;
}

static void SystemLoadWaveFileSetupCommands(NSArray* i_setupCommands, CWave* io_wave)
{
	//not setup files yet
}

static void SystemLoadWaveFileCommands(NSArray* i_commands, CWave* io_wave)
{
	//do all the commands
	//always ignore the first and last
	int setupCommandCount = [i_commands count];
	for (int i = 0; i < setupCommandCount; i++) 
	{		
		NSString *setupstring = [i_commands objectAtIndex:i];
		
		//skip empty commands
		if (setupstring == nil || [setupstring isEqualToString:@""] )
		{
			continue;
		}
		
		//split update command into parameters
		NSArray *keys = [setupstring componentsSeparatedByString: @" "];
		
		//resolve mineral placement
		NSString* key = [keys objectAtIndex:0];
		if ([key caseInsensitiveCompare:@"spawn"] == NSOrderedSame)
		{
			NSArray *parameters = [setupstring componentsSeparatedByString: @" "];			
			NSString* classNS = [parameters objectAtIndex:1];
			const char* className = [classNS UTF8String];
			float x = [[parameters objectAtIndex:2] floatValue];
			float y = [[parameters objectAtIndex:3] floatValue];
			io_wave->On_Spawn(className, x, y);
		}
		else
		{
			NSLog(@"unknown wave command ");
			NSLog(setupstring);
		}
	}
}

static bool SystemLoadWaveFile(const char* i_filename, CWave* io_level)
{
	NSString* filePath;
	GetPathForResource(i_filename, &filePath);
	
	NSString *aString = [NSString stringWithContentsOfFile:filePath];
	NSArray *strings1 = [aString componentsSeparatedByString: @"Setup:"];
	NSArray *strings2 = [aString componentsSeparatedByString: @"Commands:"];
	
	if ([strings1 count] > 1)
	{
		NSArray *setupStrings = [[strings1 objectAtIndex:1] componentsSeparatedByString: @"\n"];
		SystemLoadWaveFileSetupCommands(setupStrings, io_level);
	}
	
	if ([strings2 count] > 1)
	{
		NSArray *commandStrings = [[strings2 objectAtIndex:1]  componentsSeparatedByString: @"\n"];
		SystemLoadWaveFileCommands(commandStrings, io_level);
	}
		
	return true;
}

static bool SystemLoadMapFile(const char* i_filename, CMap* io_map)
{
	NSString* filePath;
	GetPathForResource(i_filename, &filePath);
	
	NSString *aString = [NSString stringWithContentsOfFile:filePath];
	NSArray *mapCommands = [aString componentsSeparatedByString: @"\n"];
	
	int mapCommandsCount = [mapCommands count];
	for (int i = 0; i < mapCommandsCount; i++) 
	{		
		NSString *commandString = [mapCommands objectAtIndex:i];
		
		//skip empty commands
		if (commandString == nil || [commandString isEqualToString:@""] )
		{
			continue;
		}
		
		NSArray *keys = [commandString componentsSeparatedByString: @" "];
		NSString* key = [keys objectAtIndex:0];
		if ([key caseInsensitiveCompare:@"location"] == NSOrderedSame)
		{
			const char* levelFileName = [[keys objectAtIndex:1] UTF8String];
			float x = [[keys objectAtIndex:2] floatValue];
			float y = [[keys objectAtIndex:3] floatValue];
			bool isUnlocked = [[keys objectAtIndex:4] boolValue];
			io_map->On_LocationLoad(levelFileName, x, y, isUnlocked);			
			
		}
		else if ([key caseInsensitiveCompare:@"image"] == NSOrderedSame)
		{
			const char* imageFileName = [[keys objectAtIndex:1] UTF8String];
			io_map->On_ImageLoad(imageFileName);			
			
		}
		else
		{
			NSLog(@"unknown map command ");
			NSLog(commandString);
		}
	}
	
	return true;
}

// ------------------------------------------------------------------------------
// Sounds
static bool SystemSoundLoader(const char* i_filename, unsigned int& o_soundId)
{
	NSString* filePath;
	GetPathForResource(i_filename, &filePath);
	if (filePath == nil)
	{
		NSLog(@"ERROR: No resource called %s\n", i_filename);
		return false;
	}
	
	NSURL *aFileURL = [NSURL fileURLWithPath:filePath isDirectory:NO];
	if (aFileURL != nil) 
	{
		SystemSoundID aSoundID;
		OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)aFileURL, &aSoundID);
		
		if (error == kAudioServicesNoError) 
		{ 
			o_soundId = aSoundID;
		} 
		else 
		{
			NSLog(@"ERROR: %d loading sound from path: %s\n", error, i_filename);
			return false;
		}
	}
	
    return true;
}

static bool SystemSoundPlay(unsigned int i_soundId)
{
	AudioServicesPlaySystemSound(i_soundId);
	return true;
}

// ------------------------------------------------------------------------------
static CLabel tmpLabel;
static CLabel tmpLabel2;

//static const CTextureAtlas *texAtlas;
static CTextureCounter* texCounter;

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;
@property (readonly) NSMutableDictionary* m_textures;


- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end


@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;
@synthesize m_textures;


static EAGLView *sharedSingleton;
 
+ (EAGLView *)sharedSingleton
{
  @synchronized(self)
  {
    return sharedSingleton;
  }
  return nil;
}

// You must implement this method
+ (Class)layerClass 
{
    return [CAEAGLLayer class];
}


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
    
    if ((self = [super initWithCoder:coder])) {
        // Get the layer
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
        
        eaglLayer.opaque = YES;
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
        
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
        
        if (!context || ![EAGLContext setCurrentContext:context]) {
            [self release];
            
            return nil;
        }
        
        animationInterval = 1.0 / 60.0;
    }
	
	[self setMultipleTouchEnabled:YES];
	
	isAlertShowing = false;
	sharedSingleton = self;
    
    m_textures = [[NSMutableDictionary alloc] init];
    
    // important: tell our game system how to load textures.
	CLevel::SetFileLoader(SystemLoadLevelFile);
	CWave::SetFileLoader(SystemLoadWaveFile);
	CMap::SetFileLoader(SystemLoadMapFile);
	CEngine::SetSystemMessage(SystemMessage);
	CEngine::SetSystemMessageShowing(IsSystemMessageShowing);
	CSoundMgr::SetSystemSoundLoader(SystemSoundLoader);
	CSoundMgr::SetSystemSoundPlay(SystemSoundPlay);
    
    new CEngine();


    const char* fontName = "Arial";
    const char* charSet  = "That's all there is this is the time of the original";

    SystemCreateLabel(&tmpLabel, charSet, fontName, 18);
    SystemCreateLabel(&tmpLabel2, "A comment a thing", "Arial", 12);
    texCounter = g_texturemgr.CreateTextureCounter();
    
    NSString* modelFilePath;
    
    GetPathForResource("model.bbm", &modelFilePath);
    
    if (!LoadModelDataStore(modelFilePath))
    {
        NSLog(@"ERROR: error loading file %@", modelFilePath);
    }
    
    return self;
}

- (void)drawView
{    
    [EAGLContext setCurrentContext:context];
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);

    // game view
    glMatrixMode(GL_PROJECTION);
    g_camera.Apply();
    glMatrixMode(GL_MODELVIEW);
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glDisable(GL_DEPTH_TEST);

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    NSTimeInterval timeInterval = [self.animationTimer timeInterval];
 

 	g_engine.Update((float)timeInterval);
    
    glEnable(GL_DEPTH_TEST);
    
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glDisable(GL_ALPHA_TEST);

    g_engine.Render();
 
    glDisable(GL_DEPTH_TEST);
    // The menu bar
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(0.0f, backingWidth, backingHeight, 0.0f, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_ALPHA_TEST);
    glAlphaFunc(GL_ALWAYS, 0.0f);
    g_uimgr.UIRender();
    g_texturemgr.EnableTextures(true);
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

- (void)layoutSubviews 
{
    [EAGLContext setCurrentContext:context];
    [self destroyFramebuffer];
    [self createFramebuffer];
    [self drawView];
}

- (BOOL)createFramebuffer 
{
    glGenFramebuffersOES(1, &viewFramebuffer);
    glGenRenderbuffersOES(1, &viewRenderbuffer);
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
    
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
    
    if (USE_DEPTH_BUFFER) {
        glGenRenderbuffersOES(1, &depthRenderbuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
        glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
    }
    
    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }
    
    return YES;
}


- (void)destroyFramebuffer 
{    
    glDeleteFramebuffersOES(1, &viewFramebuffer);
    viewFramebuffer = 0;
    glDeleteRenderbuffersOES(1, &viewRenderbuffer);
    viewRenderbuffer = 0;
    
    if(depthRenderbuffer) 
    {
        glDeleteRenderbuffersOES(1, &depthRenderbuffer);
        depthRenderbuffer = 0;
    }
}


- (void)startAnimation 
{
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}

- (void)stopAnimation 
{
    self.animationTimer = nil;
}

- (void)setAnimationTimer:(NSTimer *)newTimer 
{
    [animationTimer invalidate];
    animationTimer = newTimer;
}

- (void)setAnimationInterval:(NSTimeInterval)interval {
    
    animationInterval = interval;
    if (animationTimer) {
        [self stopAnimation];
        [self startAnimation];
    }
}

- (void)dealloc {
    
    [self stopAnimation];
    
    if ([EAGLContext currentContext] == context) {
        [EAGLContext setCurrentContext:nil];
    }
    
    [context release];  
    [super dealloc];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
	isAlertShowing = false;
}

// ------------------------------------------------------------------------------
#pragma mark texturestuff

-(Texture2D*)CreateTexture:(NSString*)i_name
{
    Texture2D* texture = [Texture2D alloc];// ref count?
    [texture retain];
    [i_name retain];
    [m_textures setObject:texture forKey:i_name];
    return texture;
}

-(Texture2D*)GetTexture:(NSString*)i_name
{
    return [m_textures objectForKey:i_name];
}

-(void)DeleteTexture:(GLint)i_textureId
{
    //$$$$ BB (TEF) -- NEEDS TESTING - not sure if referenceCount thing works...
    
    NSEnumerator* keyEnumerator = [m_textures keyEnumerator];
    id key;
    Texture2D* texture = nil;
    
    while ((key = [keyEnumerator nextObject]))
    {
        texture = [m_textures objectForKey:key];
        if ([texture name] == i_textureId)
        {
            break;
        }
    }
    
    if (texture != nil)
    {
        // $$$ 
        if ([texture retainCount] <= 1)
        {
            [m_textures removeObjectForKey:key];            
        }

        [texture dealloc];
    }   
}

// ------------------------------------------------------------------------------
#pragma mark interface-responses
// ------------------------------------------------------------------------------
static void getTouchPositions(UIEvent* event, Vector2f& o_touch1Pos, Vector2f& o_touch2Pos, bool prev=false)
{
    NSArray* allObjects = [[event allTouches] allObjects];
    
    UITouch* touch1 = [allObjects objectAtIndex:0];
    UITouch* touch2 = [allObjects objectAtIndex:1];

    CGPoint p1,p2;
    
    if (prev)
    {
        p1 = [touch1 locationInView:[touch1 view]];
        p2 = [touch2 locationInView:[touch2 view]];        
    }
    else
    {
        p1 = [touch1 previousLocationInView:[touch1 view]];
        p2 = [touch2 previousLocationInView:[touch2 view]];        
    }
    
    o_touch1Pos.x = p1.x; 
    o_touch1Pos.y = p1.y; 
    o_touch2Pos.x = p2.x; 
    o_touch2Pos.y = p2.y; 
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch* touch = [touches anyObject];
    NSUInteger numTaps = [touch tapCount];
    CGPoint pointInView = [touch locationInView:self];
	Vector2f pos(pointInView.x, pointInView.y);

    if ([[event allTouches] count] == 2)
    {
        Vector2f p1, p2;
        getTouchPositions(event, p1, p2);		
		g_touchmgr.RegisterPendingTouch(ETouchType_DoubleTouchStart, p1, numTaps, &p2);
    }
	else if ([[event allTouches] count] == 1)
	{
		if (numTaps < 2) 
		{
			g_touchmgr.RegisterPendingTouch(ETouchType_SingleTouchStart, pos, numTaps);
		}
		else
		{
			g_touchmgr.RegisterPendingTouch(ETouchType_DoubleTapStart, pos, numTaps);
		}   
	}
    else
    {
        m_pinching = false;
       
    }
}

float Dist(const CGPoint& p1, const CGPoint& p2)
{
    Vector2f pp1(p1.x, p1.y);
    Vector2f pp2(p2.x, p2.y);

    return Dist(pp1, pp2);
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch* touch = [touches anyObject];
	NSUInteger numTaps = [touch tapCount];
    CGPoint pointInView = [touch locationInView:self];
	Vector2f pos(pointInView.x, pointInView.y);

    if ( [[event allTouches] count] == 1)
    { 
		g_touchmgr.RegisterPendingTouch(ETouchType_SingleTouchMoved, pos, numTaps);       
    }
    else if ( [[event allTouches] count] == 2)
    {
		/*
        // (TEF) there is something I don't get here, the second touch's view is null. So these
        // touches are system wide - since we have only one view we're ok.
        NSArray* allObjects = [[event allTouches] allObjects];
        
		UITouch* touch1 = [allObjects objectAtIndex:0];
        UITouch* touch2 = [allObjects objectAtIndex:1];
        
		CGPoint p1 = [touch1 locationInView:[touch1 view]];
        CGPoint p2 = [touch2 locationInView:[touch2 view]];

		float currDistance = Dist(p1, p2);
        
        p1 = [touch1 previousLocationInView:[touch1 view]];
        p2 = [touch2 previousLocationInView:[touch2 view]];
        
        float prevDistance = Dist(p1, p2);
        
        float viewscale = g_camera.GetZoomFactor();
        static float speed = 0.005;
        viewscale += speed * (currDistance - prevDistance);
        
        if (viewscale > 3.0f)
        {
            // BB (TEF) - we should play a "dink" sound to indicate limit has been reached
            viewscale = 3.0f;
        }
        else if (viewscale < 0.025f)
        {
            // BB (TEF) - we should play a "dink" sound to indicate limit has been reached
            viewscale = 0.025f;
        }

        g_camera.SetZoomFactor(viewscale);
		*/
		
		Vector2f p1, p2;
        getTouchPositions(event, p1, p2);
		g_touchmgr.RegisterPendingTouch(ETouchType_DoubleTouchMoving, p1, numTaps, &p2);
    }

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	UITouch* touch = [touches anyObject];
	NSUInteger numTaps = [touch tapCount];
    CGPoint pointInView = [touch locationInView:self];
	Vector2f pos(pointInView.x, pointInView.y);
        
	g_touchmgr.RegisterPendingTouch(ETouchType_SingleTouchEnd, pos, numTaps);
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
	g_touchmgr.RegisterPendingTouch(ETouchType_TouchCancelled, Vector2f::Zero, 0);
}

#pragma mark networking

#if DEBUG
-(void)NNCommand:(const char*)dataBuffer
{
	NSString* commandTaker;
	NSString* commandName;
	NSString* commandParameters;

	//parse into commandtaker, command, and parameters
	NSString* commandString = [[NSString alloc] initWithCString:dataBuffer];
	
	//get the command taker name
	NSArray* commandTakers = [commandString componentsSeparatedByString: @"."];
	if ([commandTakers count] < 2)
	{
		NSLog(@"Error: No command taker withing: %s", commandString);
		return;
	}
	commandTaker = [commandTakers objectAtIndex:0];
	
	//get the command name
	NSArray* commandNames = [[commandTakers objectAtIndex:1] componentsSeparatedByString: @" "];
	if ([commandNames count] < 2)
	{
		NSLog(@"Error: No command name withing: %s", commandString);
		return;
	}
	commandName = [commandNames objectAtIndex:0];
	
	//get the command parameters
	int count = [commandTaker length] + [commandName length] + 2;
	NSRange range = NSMakeRange(count, [commandString length] - count);
	commandParameters = [commandString substringWithRange:range];

    // we need this copied into a buffer
	const char* commandTakerStr = [commandTaker UTF8String];
	const char* commandNameStr = [commandName UTF8String];
	const char* commandParametersStr = [commandParameters UTF8String];
	
	g_engine.NNCommand(commandTakerStr, commandNameStr, commandParametersStr);
	
	[commandString release];
    [commandTaker  release];
    [commandName release];
}
#endif //DEBUG

@end
